package com.wahoyu.exportwordtest.api;

import com.wahoyu.exportwordtest.domain.R;
import com.wahoyu.exportwordtest.domain.ChunkMultipartForm;
import com.wahoyu.exportwordtest.domain.SysFile;
import com.wahoyu.exportwordtest.utils.FileChunkUploadUtils;
import feign.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.SpringQueryMap;
import org.springframework.http.MediaType;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 文件服务
 *
 * @author cloudsea
 */
@FeignClient(contextId = "remoteFileService", value = "cloudsea-file", path = "/file")
public interface RemoteFileService {
    Logger log = LoggerFactory.getLogger(RemoteFileService.class);

    /**
     * 通过 Feign 调用文件下载接口
     * @param remoteFileName 文件名参数
     * ?filename=2025/5/7/c320d0a25afa55e01772dc3ab7b641e6_9b22cce0e56042a18c06920c93a1690e.docx
     * @return 包含文件字节流的响应实体
     */
    @GetMapping("/downloadFileLocal")
    Response downloadFileLocal(@RequestParam("filename") String remoteFileName);

    default R<SysFile> upload(File file){return upload(file , null);}


    default R<SysFile> upload(File file , String group){
        //每个分片的大小
        long chunkSize = 5L * 1024 * 1024;
        //文件总大小
        long fileSize = file.length();

        ChunkMultipartForm form = new ChunkMultipartForm();
        form.setFileName(file.getName());

        //小文件直接单点上传
        if(fileSize < chunkSize)
            return uploadSingle(FileChunkUploadUtils.localToMultipartFile(file),group);

        //文件唯一标识
        String uniqueIdentifier = FileChunkUploadUtils.getUniqueIdentifier(file);

        form.setFile(FileChunkUploadUtils.getFileHeadStream(file));

        R<Map<String, Object>> multipartInfo = getMultipartInfo(form, group, fileSize, chunkSize, uniqueIdentifier);

        form.setFile(null);

        //返回值不为200 ，分片异常或不支持分片上传，转换为单点上传
        if(Objects.isNull(multipartInfo) || R.SUCCESS != multipartInfo.getCode()){
            if(Objects.nonNull(multipartInfo)
                    && StringUtils.hasText(multipartInfo.getMsg())
                    && multipartInfo.getMsg().startsWith("文件类型验证失败")){
                return R.fail(multipartInfo.getMsg());
            }
            log.warn("分片上传失败，转换成单点上传{}" , Objects.nonNull(multipartInfo) ? "错误原因：" + multipartInfo.getMsg() : "。");
            return uploadSingle(FileChunkUploadUtils.localToMultipartFile(file),group);
        }

        //分片上传
        return FileChunkUploadUtils.uploadAndResult(new FileChunkUploadUtils(form, multipartInfo.getData(),file, this) ,
                form.getFileName() , fileSize) ;
    }



    /**
     * 文件上传，默认分片上传，分片上传不支持使用单点上传
     *
     * @param file
     * @param group 指定文件上传组
     * @return
     */
    default R<SysFile> upload(MultipartFile file,String group) {
        //每个分片的大小
        long chunkSize = 5L * 1024 * 1024;
        //文件总大小
        long fileSize = file.getSize();

        ChunkMultipartForm form = new ChunkMultipartForm();
        form.setFileName(file.getOriginalFilename());

        //小文件直接单点上传
        if(fileSize < chunkSize)
            return uploadSingle(file,group);

        //文件唯一标识
        String uniqueIdentifier = FileChunkUploadUtils.getUniqueIdentifier(file);

        byte[] bytes = FileChunkUploadUtils.getMultipartFileByte(file);
        //截取头部文件流
        form.setFile(FileChunkUploadUtils.getMultipartFileHeadStream(bytes , file.getOriginalFilename() , file.getContentType()));

        R<Map<String, Object>> multipartInfo = getMultipartInfo(form, group, fileSize, chunkSize, uniqueIdentifier);
        form.setFile(null);


        if(Objects.isNull(multipartInfo) || R.SUCCESS != multipartInfo.getCode()){
            if(Objects.nonNull(multipartInfo)
                    && StringUtils.hasText(multipartInfo.getMsg())
                    && multipartInfo.getMsg().startsWith("文件类型验证失败")){
                throw new RuntimeException(multipartInfo.getMsg());
            }
            log.warn("分片上传失败，转换成单点上传{}" , Objects.nonNull(multipartInfo) ? "错误原因：" + multipartInfo.getMsg() : "。");
            //返回值不为200 ，分片异常或不支持分片上传，转换为单点上传
            return uploadSingle(file,group);
        }

        //分片上传
        return FileChunkUploadUtils.uploadAndResult(new FileChunkUploadUtils(form, multipartInfo.getData(),bytes , file.getContentType(), this) ,
                form.getFileName() , fileSize) ;
    }


    default R<Map<String, Object>> getMultipartInfo(ChunkMultipartForm form ,String group,long fileSize , long chunkSize ,
                                                    String uniqueIdentifier){
        //分片数量
        long chunkCount = fileSize % chunkSize == 0 ? fileSize / chunkSize : fileSize / chunkSize + 1;
        //分片树不能大于10000
        if (chunkCount > 10000) {
            chunkSize = fileSize / 9999;
            chunkCount = fileSize % chunkSize == 0 ? 9999 : 10000;
        }

        //各分片大小
        List<Integer> chunkList = new ArrayList<>((int) chunkCount);
        for (int i = 0; i < chunkCount; i++) {
            if (i == chunkCount - 1 && fileSize % chunkSize != 0) {
                chunkList.add((int) (fileSize % chunkSize));
            } else {
                chunkList.add((int) chunkSize);
            }
        }


        form.setUniqueIdentifier(uniqueIdentifier);
        form.setChunkSize(chunkList);
        form.setGroup(group);

       return  createMultipartUpload(form);

    }



    /**
     * 上传文件
     *
     * @param file 文件信息
     * @return 结果
     */
    @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    R<SysFile> uploadSingle(@RequestPart(value = "file") MultipartFile file , @RequestParam(value = "group",required = false) String group);


    /**
     * 删除文件
     *
     * @param filename
     * @return
     */
    @PostMapping("/deleteFile")
    R<String> deleteFile(@RequestParam("filename") String filename);

    /**
     * 断点续传接口
     */

    /**
     * 返回分片上传需要的签名数据URL及 uploadId
     *
     * @param form
     * @return
     */
    @PostMapping(value = "/createMultipartUpload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    R<Map<String, Object>> createMultipartUpload(ChunkMultipartForm form);

    /**
     * 分片上传完后合并
     *
     * @param form
     * @return /
     */
    @PostMapping("/completeMultipartUpload")
    R<String> completeMultipartUpload(@RequestBody ChunkMultipartForm form);

    /**
     * 校验chunk是否上传
     *
     * @param form
     * @return
     */
    @GetMapping(value = "/chunk")
    R<List<Integer>> chunkExist(@SpringQueryMap ChunkMultipartForm form);

    /**
     * 上传指定chunk
     *
     * @param form
     * @return
     */
    @PostMapping(value = "/chunk", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    R<Boolean> chunkUpload(ChunkMultipartForm form);
}
